# @FileName  : com.py
# @Time      : 2025/4/21 16:45
# @Author    : LuZhaoHui
# @Software  : PyCharm

import serial
import serial.tools.list_ports
import asyncio
from typing import Optional, Callable
from file import *
import threading
import time
import queue
from PyQt5.QtCore import pyqtSignal, QThread


def list_ports():
    """列出所有可用串口"""
    ports = serial.tools.list_ports.comports()
    return [port.device for port in ports]


class AsyncSerial:
    def __init__(self):
        self.serial_port: Optional[serial.Serial] = None
        self.is_connected = False
        self._reader_task: Optional[asyncio.Task] = None
        self._receive_callback: Optional[Callable[[str], None]] = None
        self._loop = asyncio.get_event_loop()

    # def list_ports(self) -> list[str]:
    #     """列出所有可用串口"""
    #     ports = serial.tools.list_ports.comports()
    #     return [port.device for port in ports]

    async def connect(self, port: str, baudrate: int = 9600,
                      receive_callback: Optional[Callable[[str], None]] = None):
        """
        异步连接串口
        :param port: 串口名称 (如 'COM3' 或 '/dev/ttyUSB0')
        :param baudrate: 波特率
        :param receive_callback: 接收数据的回调函数
        """
        if self.is_connected:
            save_log('[%s]串口已连接，请先断开' % port)
            return
        try:
            self.serial_port = serial.Serial(
                port=port,
                baudrate=baudrate,
                timeout=0.1  # 非阻塞读取
            )
            self.is_connected = True
            self._receive_callback = receive_callback
            self._reader_task = asyncio.create_task(self._read_serial_data())
            self.logger.info(f"已连接到 {port}，波特率 {baudrate}")
        except Exception as e:
            self.logger.error(f"连接失败: {e}")
            raise

    async def disconnect(self):
        """断开串口连接"""
        if not self.is_connected:
            save_log('串口未连接')
            return
        self.is_connected = False
        # 取消接收任务
        if self._reader_task:
            self._reader_task.cancel()
            try:
                await self._reader_task
            except asyncio.CancelledError:
                pass
        # 关闭串口
        if self.serial_port:
            self.serial_port.close()
            save_log('串口已断开')

    async def send_data(self, data: str):
        """发送数据"""
        if not self.is_connected or not self.serial_port:
            self.logger.error("未连接串口")
            return

        try:
            self.serial_port.write(data.encode('utf-8'))
            self.logger.debug(f"发送: {data}")
        except Exception as e:
            self.logger.error(f"发送失败: {e}")
            raise

    async def _read_serial_data(self):
        """异步读取串口数据"""
        buffer = ""
        while self.is_connected and self.serial_port:
            try:
                # 异步等待数据可用
                await self._loop.run_in_executor(
                    None,
                    lambda: self.serial_port.in_waiting or asyncio.sleep(0.01)
                )

                if self.serial_port.in_waiting > 0:
                    raw_data = self.serial_port.read(self.serial_port.in_waiting)
                    data = raw_data.decode('utf-8', errors='ignore')

                    # 处理完整行或缓冲区
                    buffer += data
                    while '\n' in buffer:
                        line, buffer = buffer.split('\n', 1)
                        line = line.strip()
                        if line:
                            self.logger.debug(f"接收: {line}")
                            if self._receive_callback:
                                # 在主线程中调用回调
                                self._loop.call_soon_threadsafe(
                                    self._receive_callback,
                                    line
                                )
            except Exception as e:
                self.logger.error(f"接收数据错误: {e}")
                break


async def example_usage():
    """使用示例"""
    serial_manager = AsyncSerial()

    # 列出可用串口
    ports = serial_manager.list_ports()
    print("可用串口:", ports)

    if not ports:
        print("未检测到可用串口！")
        return

    # 定义接收回调函数
    def on_data_received(data: str):
        print(f"回调接收: {data}")

    try:
        # 连接第一个可用串口
        await serial_manager.connect(
            port=ports[0],
            baudrate=9600,
            receive_callback=on_data_received
        )

        # 发送测试数据
        await serial_manager.send_data("Hello, Serial Port!\n")

        # 保持运行以接收数据
        print("运行中...按 Ctrl+C 退出")
        while True:
            await asyncio.sleep(1)

    except asyncio.CancelledError:
        pass
    finally:
        # 断开连接
        await serial_manager.disconnect()


# 串口通信类
class SerialThread(QThread):
    # 定义信号： 接收，错误，关闭
    data_send = pyqtSignal(str)
    data_recv = pyqtSignal(str)
    error_occurred = pyqtSignal(str)
    change_queue = pyqtSignal(int)
    change_state = pyqtSignal(bool)
    finished = pyqtSignal()

    def __init__(self, port, baudrate):
        super().__init__()
        self.port = port
        self.baudrate = baudrate
        self.ser = serial.Serial(port, baudrate, timeout=1)
        self.running = False
        self.queue = queue.Queue()
        self.isOk = False
        self.isArrival = False      # 到达跟踪

    def isEmpty(self):
        return self.queue.empty()

    def send(self, data, ok=True):
        self.data_send.emit(data)
        if '143' in data:
            self.isArrival = True
        data += '\n'
        self.ser.write(data.encode('utf-8'))
        if self.isOk:
            self.isOk = ok
            self.change_state.emit(self.isOk)

    def run(self):
        self.change_queue.emit(self.queue.qsize())
        self.change_state.emit(self.isOk)
        self.running = True
        while self.running:
            try:
                if not self.queue.empty() and not self.isOk:
                    # 发送
                    data = self.queue.get()
                    self.change_queue.emit(self.queue.qsize())
                    self.send(data)
                if self.ser.in_waiting:
                    # 接收
                    data = self.ser.readline().decode('utf-8').strip()
                    self.data_recv.emit(data)
                    if self.isArrival:
                        # 跟踪
                        if 'Arrival' in data:
                            self.isArrival = False
                    if 'ok' in data.lower():
                        self.isOk = False
                        self.change_state.emit(self.isOk)
                # 避免过快的循环导致资源浪费
                time.sleep(0.1)
            except Exception as e:
                print(e)
                self.error_occurred.emit(str(e))
        self.finished.emit()
        self.ser.close()

    def write(self, data):
        self.queue.put(data)
        self.change_queue.emit(self.queue.qsize())

    def stop(self):
        self.running = False


# def example_thread():
#     serial_thread = SerialThread('COM1', 9600)  # 替换为你的串口和波特率
#     serial_thread.data_received.connect(self.on_data_received)
#     serial_thread.error_occurred.connect(self.on_error)
#     serial_thread.finished.connect(self.on_finished)

# def on_data_received(self, data):
#     self.text_edit.append(data)


# def on_error(self, error):
#     print("Error:", error)  # 可以改为 GUI 提示框等操作


# def on_finished(self):
#     print("Serial thread finished.")  # 可以改为 GUI 提示等操作


# def stop_serial(self):
#     self.serial_thread.stop()  # 停止串口线程和接收数据流
#     print("Serial stopped.")  # 可以改为 GUI 提示等操作


if __name__ == "__main__":
    # 异步
    try:
        asyncio.run(example_usage())
    except KeyboardInterrupt:
        print("\n程序已终止")
